#pragma once
#include "Object3D.h"
#include "Renderer.h"

class SkyBox : public Object3D
{
public:
    SkyBox();
    void render() override;

    uint32_t cubeTexture;
    Nitro::Renderer::Buffer vertexBuffer;
    Nitro::Renderer::Shader vs, fs;
    Nitro::Renderer::Pipeline pipeline;
};

SkyBox::SkyBox()
{
    using namespace Nitro::Renderer;

    float verts[] = {
        -1.0f, 1.0f,  -1.0f, //
        -1.0f, -1.0f, -1.0f, //
        1.0f,  -1.0f, -1.0f, //
        1.0f,  -1.0f, -1.0f, //
        1.0f,  1.0f,  -1.0f, //
        -1.0f, 1.0f,  -1.0f, //

        -1.0f, -1.0f, 1.0f,  //
        -1.0f, -1.0f, -1.0f, //
        -1.0f, 1.0f,  -1.0f, //
        -1.0f, 1.0f,  -1.0f, //
        -1.0f, 1.0f,  1.0f,  //
        -1.0f, -1.0f, 1.0f,  //

        1.0f,  -1.0f, -1.0f, //
        1.0f,  -1.0f, 1.0f,  //
        1.0f,  1.0f,  1.0f,  //
        1.0f,  1.0f,  1.0f,  //
        1.0f,  1.0f,  -1.0f, //
        1.0f,  -1.0f, -1.0f, //

        -1.0f, -1.0f, 1.0f, //
        -1.0f, 1.0f,  1.0f, //
        1.0f,  1.0f,  1.0f, //
        1.0f,  1.0f,  1.0f, //
        1.0f,  -1.0f, 1.0f, //
        -1.0f, -1.0f, 1.0f, //

        -1.0f, 1.0f,  -1.0f, //
        1.0f,  1.0f,  -1.0f, //
        1.0f,  1.0f,  1.0f,  //
        1.0f,  1.0f,  1.0f,  //
        -1.0f, 1.0f,  1.0f,  //
        -1.0f, 1.0f,  -1.0f, //

        -1.0f, -1.0f, -1.0f, //
        -1.0f, -1.0f, 1.0f,  //
        1.0f,  -1.0f, -1.0f, //
        1.0f,  -1.0f, -1.0f, //
        -1.0f, -1.0f, 1.0f,  //
        1.0f,  -1.0f, 1.0f,  //
    };

    BufferDescriptor bufferDesc;
    bufferDesc.bindFlags = BindFlags::VertexBuffer;
    bufferDesc.size = sizeof(verts);
    bufferDesc.appendAttribute(0, 3 * sizeof(float), Format::RGB32Float);
    vertexBuffer = CreateBuffer(bufferDesc, verts);

    glGenTextures(1, &cubeTexture);
    glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);
    int w, h, n;
    stbi_uc* data;
    for (uint32_t i = 1; i <= 6; ++i)
    {
        auto filename = std::to_string(i) + ".png";
        data = stbi_load(filename.c_str(), &w, &h, &n, 3);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, w, h, 0,
                     GL_RGB, GL_UNSIGNED_BYTE, data);
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

    ShaderDescriptor shaderDesc;
    shaderDesc.type = ShaderType::VertexShader;
    shaderDesc.sourceType = ShaderSourceType::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    in vec3 position;
    out vec3 texCoord;
    uniform mat4 MVP;
    void main() {
        gl_Position = (vec4(position, 1.0f)).xyww;
        texCoord = position;
    }
    )delimiter";
    vs = CreateShader(shaderDesc);

    shaderDesc.type = ShaderType::FragmentShader;
    shaderDesc.source = R"delimiter(
    #version 450 core
    in vec3 texCoord;
    out vec4 color;
    uniform samplerCube skybox;
    void main() {
        color = vec4(1.0f);
    }
    )delimiter";
    fs = CreateShader(shaderDesc);

    PipelineDescriptor pipelineDesc;
    pipelineDesc.vertexShader = vs;
    pipelineDesc.fragmentShader = fs;
    pipelineDesc.drawMode = DrawMode::Triangles;
    pipeline = CreatePipeline(pipelineDesc);
}

void SkyBox::render()
{
    updateMatrix();
    pipeline.activate();
    glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);
    pipeline.setVertexBuffer(vertexBuffer);
    pipeline.setUniform("MVP", viewProjectionMatrix * modelMatrix);
    pipeline.draw(0, 36);
}